import serial
import time
import re

class TapingMachineController:
    # 状态码映射表
    PROCESS_STATUS_MAP = {
        0: 'Idle',
        1: 'Single Work',
        2: 'Repeat Work',
        3: 'Error',
        4: 'Finish'
    }
    
    HEATER_STATUS_MAP = {
        0: 'Heater is off',
        1: 'Heater is ready',
        2: 'Heater is Heating',
        3: 'Heater is Cooling'
    }
    
    ERROR_CODE_MAP = {
        0: 'No Error',
        2: 'ERR_HMOTOR_UP',
        3: 'ERR_DMOTOR_CLOSE',
        4: 'ERR_DMOTOR_OPEN',
        5: 'ERR_HEATER_SENSOR',
        6: 'ERR_HEATER_RAMP',
        7: 'ERR_HEATER_HOLD',
        9: 'ERR_DMOTOR_OVER',
        10: 'ERR_HMOTOR_OVER',
        11: 'ERR_HSENSOR_MISS',
        12: 'ERR_SYSTEM_FW',
        13: 'ERR_SYSTEM_HW'
    }
    
    WARNING_CODE_MAP = {
        0: 'No Warning',
        2: 'WARN_SEAL_ROLL_ON',
        3: 'WARN_DMOTOR_NOT_OPENED',
        4: 'WARN_DOOR_IS_OPENED',
        5: 'WARN_DOOR_NOT_CLOSED',
        6: 'WARN_PRESSURE_SENSOR',
        7: 'WARN_SEAL_FILM_EXHAUST',
        8: 'WARN_SEAL_ROLL_OFF',
        9: 'WARN_HMOTOR_DOWN'
    }
    
    SENSOR_STATUS_MAP = {
        0x01: 'Shuttle middle sensor',
        0x02: 'Shuttle open sensor',
        0x04: 'Shuttle close sensor',
        0x08: 'Clean door sensor',
        0x10: 'Seal roll sensor',
        0x20: 'Heater motor up sensor',
        0x40: 'Heater motor down sensor'
    }
    def __init__(self, port='/dev/Seal', baudrate=19200):
        """
        初始化串口连接。
        
        :param port: 设备的串口号，默认'/dev/ttyUSB0'
        :param baudrate: 波特率，默认19200
        """
        self.ser = serial.Serial(port, baudrate, bytesize=serial.EIGHTBITS,
                                 parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
        time.sleep(2)  # 等待串行连接初始化
        
    # def _calculate_checksum(self, cmd_str):
    #     """校验和固定为'zz', 跳过校验环节"""
    #     return 'zz'
        
    def _calculate_checksum(self, data: str) -> str:
        """
        计算给定字符串的校验码（两个字母组成的字符串）
        :param data: 需要计算校验的数据部分（不包含起始、结束符和校验位）
        :return: 校验码（如 'HO'）
        """
        checksum = 0xFFFF
        for ch in data:
            checksum -= ord(ch)
            checksum &= 0xFFFF

        final_val = (checksum + 0xB6) & 0xFF
        high, low = divmod(final_val, 0x10)
        return chr(0x41 + high) + chr(0x41 + low)

    def send_command(self, command, index='00', params=None):
        """
        发送命令给设备并接收响应。
        
        :param command: 命令代码
        :param index: 命令索引，默认'00'
        :param params: 参数列表
        :return: 响应字符串
        """
        if params is None:
            params = []
        
        # 构造命令字符串
        cmd_str = f"*{index}{command}="
        for param in params:
            cmd_str += f"{param},"
        cmd_str = cmd_str.rstrip(',') + "!"
        
        checksum = self._calculate_checksum(cmd_str)
        full_cmd = f"{cmd_str[:-1]}{checksum}!"  # 将校验和插入到命令中
        
        print(f"[DEBUG] Sending command: {full_cmd}")
        self.ser.write(full_cmd.encode('ascii'))
        
        # 等待并验证确认响应
        confirmation_response = self._wait_for_confirmation(index=index)
        if not confirmation_response:
            raise RuntimeError("Failed to receive confirmation response after sending command.")
        
        # 确认响应成功后延时 0.1 秒
        time.sleep(0.1)

        return confirmation_response

    def _parse_response(self, response):
        """
        解析设备响应，并验证校验和。
        
        :param response: 原始响应字符串
        :return: 解析并验证后的数据部分
        """
        print(f"[DEBUG] Parsing response: {response}")
        if ord(response[0]) == 0x0D:
            print("[DEBUG] Response starts with 0x0D, skipping")
            response = response[1:]

        if not response.startswith('*') or not response.endswith('!'):
            raise ValueError(f"Invalid response format: missing start '*' or end '!'")

        if len(response) < 4:
            raise ValueError("Response too short to contain valid checksum")

        # 提取数据部分和校验码
        data_part = response[1:-3]         # 去掉开头 * 和结尾 ! 及其前面两个字符（校验码）
        checksum_received = response[-3:-1].upper()  # 去掉最后的 '!'，获取校验码

        print(f"[DEBUG] Extracted data part: {data_part}")
        print(f"[DEBUG] Received checksum: {checksum_received}")

        # 特殊处理确认指令
        if data_part.startswith("Y00") and (checksum_received == 'ZZ' or checksum_received == 'PM'):
            print("[INFO] Confirmation received, skipping checksum verification")
            return data_part

        if checksum_received == 'ZZ':
            print("[INFO] Checksum skipped (received 'zz')")
            return data_part

        # 计算实际校验码并比对
        calculated_checksum = self._calculate_checksum(data_part)
        print(f"[DEBUG] Calculated checksum: {calculated_checksum}")

        if calculated_checksum != checksum_received:
            raise ValueError(f"Checksum mismatch: expected {calculated_checksum}, got {checksum_received}")

        print("[INFO] Checksum verification passed")
        return data_part

    def _wait_for_confirmation(self, index='00', timeout=1):
        """
        等待并验证确认响应。
        
        :param index: 命令索引
        :param timeout: 超时时间（秒）
        :return: 确认响应是否成功
        """
        start_time = time.time()
        while time.time() - start_time < timeout:
            raw_response = self.ser.read_until(b'!')
            decoded_response = raw_response.decode('ascii').strip()
            print(f"[DEBUG] Received raw confirmation response: {decoded_response}")
            
            try:
                parsed_data = self._parse_response(decoded_response)
                # 对于确认响应，检查是否是以 Y 开头并且后跟正确的索引
                if parsed_data.startswith(f"Y{index}"): #and (parsed_data.endswith("PM") or parsed_data.endswith("ZZ")):
                    print(f"[INFO] Confirmation received: {decoded_response}")
                    return True
            except Exception as e:
                print(f"[ERROR] Failed to parse confirmation: {e}")
        
        print(f"[ERROR] Timeout waiting for confirmation response.")
        return False

    def get_device_status(self, parsed_response):
        """
        获取设备主动反馈的状态信息。
        
        :return: 解析后的状态字典
        """
        try:
            # 监听设备反馈的状态信息 
            parsed_response = parsed_response[1:]   #  去除命令起始位“T”
            fields = parsed_response.split('=')
            if len(fields) < 2:
                raise ValueError(f"Incomplete status response: {parsed_response}")
            
            status_fields = fields[1].split(',')
            if len(status_fields) < 7:
                raise ValueError(f"Incomplete status fields: {status_fields}")
            
            # 解析并映射状态码
            temperature = int(status_fields[0])
            process_status = self.PROCESS_STATUS_MAP.get(int(status_fields[1]), f'Unknown Process Status ({status_fields[1]})')
            heater_status = self.HEATER_STATUS_MAP.get(int(status_fields[2]), f'Unknown Heater Status ({status_fields[2]})')
            error_code = self.ERROR_CODE_MAP.get(int(status_fields[3]), f'Unknown Error Code ({status_fields[3]})')
            warning_code = self.WARNING_CODE_MAP.get(int(status_fields[4]), f'Unknown Warning Code ({status_fields[4]})')
            sensor_status = ', '.join([self.SENSOR_STATUS_MAP[int(bit)] for bit in bin(int(status_fields[5]))[2:] if int(bit) and int(bit) in self.SENSOR_STATUS_MAP])
            heating_time = fields[0]
            
            status = {
                'temperature': temperature,
                'process_status': process_status,
                'heater_status': heater_status,
                'error_code': error_code,
                'warning_code': warning_code,
                'sensor_status': sensor_status,
                'heating_time': heating_time
            }
            
            print(f"[DEBUG] Parsed device status: {status}")
            return status
        except Exception as e:
            print(f"[ERROR] Error parsing device status: {e}")
            return None

    def parse_regular_data_response(self, parsed_response):
        """
        解析设备返回的常规数据响应。
        
        :param raw_response: 原始响应字符串
        :return: 解析后的数据字典
        """
        try:
            # if parsed_response.startswith('D'):    
            parsed_response = parsed_response[1:]   #  去除命令起始位“D”
            # 分割响应字符串
            fields = parsed_response.split('=')
            if len(fields) < 2:
                raise ValueError(f"Incomplete regular data response: {parsed_response}")
            
            # 进一步分割具体数据字段
            data_fields = fields[1].split(',')
            if len(data_fields) != 2:
                raise ValueError(f"Incorrect number of fields in regular data response: {data_fields}")
            
            fw_version = fields[0]                   # 固件版本长度为4
            work_time = int(data_fields[0])          # 工作时间
            work_count = int(data_fields[1])         # 工作计数
            
            # 返回解析后的数据字典
            return {
                'fw_version': fw_version,
                'work_time': work_time,
                'work_count': work_count
            }
        except Exception as e:
            print(f"[ERROR] Error parsing regular data response: {e}")
            return None

    def get_device_version(self):
        while True:
            try:
                raw_response = self.ser.read_until(b'!')
                decoded_response = raw_response.decode('ascii').strip()
                # print(f"[DEBUG] Checking readiness with response: {decoded_response}")
                parsed_data = self._parse_response(decoded_response)

                if not parsed_data.startswith('D'):
                    continue
                device_sersion = self.parse_regular_data_response(parsed_data)
                if device_sersion:
                    print(f"[INFO] Device sersion: {device_sersion}")
                    break
            except Exception as e:
                print(f"[ERROR] Error reading device sersion: {e}")

    def is_ready_for_sealing(self, target_temperature):
        """
        检查设备是否准备好进行封膜操作。
        
        :param target_temperature: 目标封膜温度
        :return: 是否准备好
        """
        while True:
            raw_response = self.ser.read_until(b'!')
            decoded_response = raw_response.decode('ascii').strip()
            # print(f"[DEBUG] Checking readiness with response: {decoded_response}")
            parsed_data = self._parse_response(decoded_response)

            try:
                if parsed_data[0] != 'T':
                    continue
                status = self.get_device_status(parsed_data)
                if status:
                    if status['error_code'] != 'No Error':
                        print(f"[ERROR] Device has error: {status['error_code']}")
                        return False

                    if status['heater_status'] != 'Heater is ready':
                        print(f"[ERROR] Heater is not ready: {status['heater_status']}, current temperature: {status['temperature']}°C, target temperature: {target_temperature}°C.")
                        return False

                    if status['heater_status'] == 'Heater is Heating':
                        print(f"[INFO] Heater is Heating: {status['temperature']}°C, target temperature: {target_temperature}°C.")
                        time.sleep(5)
                        continue
                    
                    print("[INFO] Device is ready for sealing.")
                    return True
            except Exception as e:
                print(f"[ERROR] Error during readiness check: {e}")
                return False

    def is_sealing_complete(self):
        while True:
            try:
                raw_response = self.ser.read_until(b'!')
                decoded_response = raw_response.decode('ascii').strip()
                # print(f"[DEBUG] Checking readiness with response: {decoded_response}")
                parsed_data = self._parse_response(decoded_response)
                if parsed_data[0] != 'T':
                    continue
                
                status = self.get_device_status(parsed_data)
                if status['status'] == 'Finsh':
                    return True
                else:
                    print("[WARNING] Invalid response format")
            except Exception as e:
                print(f"[ERROR] Error reading from serial port: {e}")
                return False

    # def is_ready_for_sealing(self, target_temperature):
    #     """
    #     检查设备是否准备好进行封膜操作。
        
    #     :param target_temperature: 目标封膜温度
    #     :return: 是否准备好
    #     """
    #     while True:
    #         raw_response = self.ser.read_until(b'!')
    #         decoded_response = raw_response.decode('ascii').strip()
    #         print(f"[DEBUG] Checking readiness with response: {decoded_response}")
    #         parsed_data = self._parse_response(decoded_response)
    #         try:
    #             status = self.get_device_status(parsed_data)
    #             if status:
    #                 if status['error_code'] != 'No Error':
    #                     print(f"[ERROR] Device has error: {status['error_code']}")
    #                     return False
    #                 # if status['temperature'] < target_temperature:
    #                 #     print(f"[ERROR] Current temperature ({status['temperature']}°C) is below target ({target_temperature}°C).")
    #                 #     return False
    #                 if status['heater_status'] != 'Heater is ready':
    #                     print(f"[ERROR] Heater is not ready: {status['heater_status']}, current temperature: {status['temperature']}°C, target temperature: {target_temperature}°C.")
    #                     return False
    #                 # if status['heater_status'] not in ['Heater is ready', 'Heater is Heating']:
    #                 #     print(f"[ERROR] Heater is not ready (status: {status['heater_status']}).")
    #                 #     return False
                    
    #                 print("[INFO] Device is ready for sealing.")
    #                 return True
    #             # else:
    #                 # print(f"[WARNING] Unexpected response type during readiness check: {parsed_data}")
    #         except Exception as e:
    #             print(f"[ERROR] Error during readiness check: {e}")
    #             return False


    def close(self):
        """关闭串口连接"""
        self.ser.close()

    def set_sealing_time(self, seconds):
        """
        设置封膜时间。
        
        :param seconds: 封膜时间（秒），范围需符合设备要求
        """
        if 1 <= seconds <= 999:  # 假设时间范围在1到999秒之间
            formatted_time = f"{seconds:04d}"  # 格式化为4位数字
            print("[INFO] Setting sealing time...")
            self.send_command(command="DT", params=[formatted_time])
        else:
            raise ValueError("Sealing time out of range. Must be between 1 and 999.")

    def set_sealing_temperature(self, temperature):
        """
        设置封膜温度。
        
        :param temperature: 封膜温度（度），范围需符合设备要求
        """
        if 100 <= temperature <= 200:  # 假设温度范围在100到200度之间
            formatted_temp = f"{temperature:04d}"  # 格式化为4位数字
            print("[INFO] Setting sealing temperature...")
            self.send_command(command="DH", params=[formatted_temp])
        else:
            raise ValueError("Temperature out of range. Must be between 100 and 200.")

    def start_sealing(self):
        """开始封膜操作"""
        print("[INFO] Starting sealing operation...")
        self.send_command(command="GS")